--------------Crypto Cube--------------
A 4am crack                  2016-01-28
---------------------------------------

Name: Crypto Cube
Genre: educational
Year: 1984
Publisher: DesignWare, Inc.
Media: single-sided 5.25" floppy disk
OS: custom
Previous cracks: none
Identical cracks:
  #525 The Great Computer GameWorks
  #228 Creature Creator
  #193 Mission: Algebra

                   ~

               Chapter 0
 In Which Various Automated Tools Fail
          In Interesting Ways


COPYA
  no read errors, but copy grinds and
  eventually crashes

Locksmith Fast Disk Backup
  ditto

EDD 4 bit copy (no sync, no count)
  ditto

Copy ][+ nibble editor
  nothing suspicious (standard prologue
  and epilogue on address and data
  fields)

Disk Fixer
  can read every sector
  T00 sectors are in an odd order
  no sign of DOS or ProDOS
  no disk catalog on any track

Why don't my copies work?
  I don't know. Maybe a nibble check
  that reads a decryption key (some
  Epyx games) or a seed to calculate
  the proper track/sector to load next
  (some Activision games).

                   ~

               Chapter 1
   In Which We Fail To Find A Nibble
   Check But Find Something Far More
          Interesting Instead


[S6,D1=original disk]
[S5,D1=my work disk]

]PR#5
...
CAPTURING BOOT0
...reboots slot 6...
...reboots slot 5...
SAVING BOOT0
CAPTURING BOOT1
...reboots slot 6...
...reboots slot 5...
SAVING BOOT1
SAVING RWTS

No nibble check auto-detected. Bummer.

]BLOAD BOOT1,A$2600
]CALL -151

*FE89G FE93G     ; disconnect DOS

*B600<2600.2FFFM ; move RWTS into place

*B700L

; normal initialization stuff here,
; setting up the RWTS parameter table
; at $B7E8
B700-   8E E9 B7    STX   $B7E9
B703-   8E F7 B7    STX   $B7F7
B706-   A9 01       LDA   #$01
B708-   8D F8 B7    STA   $B7F8
B70B-   8D EA B7    STA   $B7EA
B70E-   AD E0 B7    LDA   $B7E0
B711-   8D E1 B7    STA   $B7E1
B714-   A9 00       LDA   #$00
B716-   8D EC B7    STA   $B7EC
.
.
.
B738-   20 00 BB    JSR   $BB00   ; hmm

*BB00L

BB00-   86 1B       STX   $1B

; neat trick to get the high byte of
; the return address from the stack
BB02-   BA          TSX
BB03-   BD 02 01    LDA   $0102,X

; $B7 --> zero page $03
BB06-   85 03       STA   $03
BB08-   18          CLC
BB09-   69 04       ADC   #$04

; $BB --> zero page $05
BB0B-   85 05       STA   $05
BB0D-   A0 00       LDY   #$00
BB0F-   84 02       STY   $02
BB11-   A0 40       LDY   #$40
BB13-   84 04       STY   $04
BB15-   A0 39       LDY   #$39
BB17-   A9 93       LDA   #$93

; $93 --> $B739
BB19-   91 02       STA   ($02),Y
BB1B-   C8          INY
BB1C-   A5 03       LDA   $03

; $B7 --> $B73A
BB1E-   91 02       STA   ($02),Y

; $BB40..$BBBF --> $0800..$087F
BB20-   A0 7F       LDY   #$7F
BB22-   B1 04       LDA   ($04),Y
BB24-   99 00 08    STA   $0800,Y
BB27-   88          DEY
BB28-   10 F8       BPL   $BB22

; restore X register and jump to
; relocated code
BB2A-   A6 1B       LDX   $1B
BB2C-   4C 00 08    JMP   $0800

I need to know what ends up at $0800.

*BB2C:4C 59 FF     ; break into monitor
*B738G
<beep>

*800L

0800-   A9 00       LDA   #$00
0802-   A0 EC       LDY   #$EC

; ($02) points to $B700 at this point,
; so this looks like it's setting up
; the RWTS parameter table again.
; $B7EC holds the track number (0).
0804-   91 02       STA   ($02),Y
0806-   C8          INY

; $B7ED holds the sector number (0).
0807-   91 02       STA   ($02),Y
0809-   A9 09       LDA   #$09
080B-   A0 F1       LDY   #$F1

; $B7F1 holds the destination address,
; so it looks like we're going to read
; a bunch of sectors from track 0
; into $0900 and up.
080D-   91 02       STA   ($02),Y
080F-   A9 08       LDA   #$08
0811-   A0 E1       LDY   #$E1

; $B7E1 holds the number of sectors
; to read (8).
0813-   91 02       STA   ($02),Y

; set up a JMP instruction at $01 (WTF)
0815-   A9 4C       LDA   #$4C
0817-   85 01       STA   $01
0819-   A9 93       LDA   #$93
081B-   85 02       STA   $02

; call $B793 to read multiple sectors
081D-   20 01 00    JSR   $0001

Despite the odd indirection, this part
isn't suspicious at all. It's using the
standard multi-read routine at $B793 to
read the rest of track 0 into memory.
But look what comes next:

0820-   A0 00       LDY   #$00
0822-   84 02       STY   $02
0824-   A5 1E       LDA   $1E
0826-   85 08       STA   $08
0828-   0A          ASL
0829-   90 01       BCC   $082C
082B-   C8          INY
082C-   48          PHA
082D-   68          PLA
082E-   D0 F8       BNE   $0828
0830-   C0 02       CPY   #$02
0832-   69 FF       ADC   #$FF
0834-   85 1F       STA   $1F

Wait a minute. The standard DOS 3.3
RWTS doesn't use zero page $1E, so at
this point it should be uninitialized
and essentially random. But this loop
seems to be doing some bit math on it
(specifically, counting how many bits
are zero and storing that in the Y
register), so either something is
setting $1E or I'm hallucinating. Or
possibly both.

Disk Fixer
  --> [F]ind
    --> [H]ex
      --> "1E"

I found a few instances of $1E in track
0. The only one that looked like actual
RWTS code was T00,S0F,$3F. After some
manual cross-referencing, it appears
that this sector is loaded at $B900 by
the RWTS loader bootstrap in sector 0.
(Remember, the sectors are in a weird
order.) Here is the relevant code,
which is supposed to be checking the
data epilogue:

*B92FL

B92F-   BD 8C C0    LDA   $C08C,X
B932-   10 FB       BPL   $B92F
B934-   B8          CLV
B935-   BD 8C C0    LDA   $C08C,X
B938-   10 FB       BPL   $B935
B93A-   B8          CLV
B93B-   1E 8C C0    ASL   $C08C,X
B93E-   26 1E       ROL   $1E
B940-   50 5C       BVC   $B99E
B942-   38          SEC
B943-   60          RTS

Normally, this code would be checking
for the nibble sequence "DE AA", the
standard epilogue after the data
field. But instead, this disk accepts
any two nibbles, then does some bit
math to twiddle zero page $1E based on
the timing bits that follow. Since that
relocated routine at $0800 reads 8
sectors, zero page $1E gets twiddled 8
times, giving it a fully defined value
despite never being initialized.

Never a dull moment in the land of
Apple II copy protection.

Obviously the value in zero page $1E is
important. Let's capture it.

]PR#5
]CALL -151

*9600<C600.C6FFM

; set up callback after boot0
96F8-   A9 4C       LDA   #$4C
96FA-   8D 4A 08    STA   $084A
96FD-   A9 0A       LDA   #$0A
96FF-   8D 4B 08    STA   $084B
9702-   A9 97       LDA   #$97
9704-   8D 4C 08    STA   $084C
9707-   4C 01 08    JMP   $0801

; set up callback after mystery routine
; at $BB00
970A-   A9 4C       LDA   #$4C
970C-   8D 60 BB    STA   $BB60
970F-   A9 1C       LDA   #$1C
9711-   8D 61 BB    STA   $BB61
9714-   A9 97       LDA   #$97
9716-   8D 62 BB    STA   $BB62
9719-   4C 00 B7    JMP   $B700

; print value of zero page $1E
971C-   A5 1E       LDA   $1E
971E-   20 DA FD    JSR   $FDDA

; turn off disk motor and exit
9721-   AD E8 C0    LDA   $C0E8
9724-   4C 59 FF    JMP   $FF59

*BSAVE CAPTURE 1E,A$9600,L$127

*9600G
...
F7
<beep>

This is repeatable; the original disk
always ends up with $F7 in zero page
$1E. If my theory is correct (that this
is the crux of the copy protection),
then running the same capture utility
on my non-working copy should yield a
different result.

[S6,D1=non-working copy]
[S5,D1=my work disk]

]PR#5
]BRUN CAPTURE 1E
...
00
<beep>

All my copies (even the EDD 4 bit copy)
end up with $00 in zero page $1E.

                   ~

               Chapter 2
 In Which We Remove All Traces Of Copy
Protection Using An Automated Tool That
   I Wrote For Just Such An Occasion


[S6,D1=non-working copy]
[S5,D1=my work disk]

]PR#5
]BRUN PDP

T00,S03,$64 change A51E to A9F7

(The patch ignores the value of zero
page $1E and always loads a hard-coded
value of $F7 instead.)

Quod erat liberandum.

---------------------------------------
A 4am crack                     No. 596
------------------EOF------------------
